#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "LevelReader.h"
#include "include.h"
#include "ClosedObject.h"
#include "PinnedObject.h"
#include "Border.h"
#include "Material.h"
#include "RotatingObject.h"
#include "TranslatingObject.h"
#include "ConditionalTranslatingObject.h"
#include "PressureObject.h"
#include "NavigationEdgeList.h"
#include "CharacterObject.h"

void LevelReader::LoadLevel(TextureManager * textureManager, ObjectList ** objectList, MaterialList ** materialList, char * filename)
{
	FILE * fptr = NULL;
	printf("Opening file\n");
	if(!(fptr = fopen(filename, "r")))
	{
		printf("Level file %s is new\n", filename);
		return;
	}
	printf("File is open\n");
	char * tag;
	while((tag = GetNextTag(fptr)) != NULL)
	{
		printf("TAG: %s\n", tag);
		//read through the tag, one element at a time
		int pos = 0;		
		char * element = GetNextElement(tag, &pos);
		
		printf("ELEMENT: %s\n", element);
		//this is the tag's name. Use this to decide how to handle
		//the other parameters
		
		switch(GetElementType(element))
		{
			case BORDER_TYPE: 
				HandleBorderTag(objectList, tag, pos);
				break;
			case PLAYER_TYPE:
				//HandlePlayerTag(camera, player, supplyBag, tag, pos);
				break;
			case OBJECT_TYPE:
				printf("ITS an object\n");
				HandleObjectTag(textureManager, objectList, materialList, tag, pos);
				break;
			case CHARACTER_TYPE:
				HandleCharacterObjectTag(textureManager, objectList, tag, pos);
				break;
			case ENEMY_TYPE:
				//HandleEnemyTag(objectList, (*player), tag, pos);
				break;
			case PICKUP_TYPE:
				//HandlePickupTag(spriteList, tag, pos);
				break;
			case BACKGROUND_TYPE:
				//HandleBackgroundTag(camera, tag, pos);
				break;
			case MATERIAL_TYPE:
				HandleMaterialTag(materialList,tag, pos);
				break;
		}
			
		if(element != NULL)
			free(element);
		element = NULL;
		if(tag != NULL)
			free(tag);
		tag = NULL;
	}
	fclose(fptr);
}
int LevelReader::GetElementType(char * element)
{
	if(strcmp(element, "border") == 0)
		return BORDER_TYPE;
	if(strcmp(element,"object") == 0)
		return OBJECT_TYPE;
	if(strcmp(element, "character") == 0)
		return CHARACTER_TYPE;
	if(strcmp(element,"material") == 0)
		return MATERIAL_TYPE;
	return UNKNOWN_TYPE;
}
void LevelReader::HandleBorderTag(ObjectList ** objectList, char * tag, int pos)
{
	int numPoints = ReadNextInt(tag, &pos);
	Vector2D * vertices = ReadNextVectorArray(numPoints, tag, &pos);
	
	VertexList * vertexList = new VertexList();
	if(vertices)
	{
		for(int i=0; i < numPoints; i++)
		{
			vertexList->Add(new Vertex(vertices[i]));	
		}
		Border* border = new Border(vertexList);
		if(*objectList)
		{
			(*objectList)->Add(border);
		}
		else
		{	
			*objectList = new ObjectList(border);
		}
		delete vertices;
		vertices = NULL;
	}
}
int LevelReader::GetObjectType(char * name)
{
	if(strcmp(name, "baseobject") == 0)
		return BASE_OBJECT_TYPE;
	if(strcmp(name, "pinnedobject") == 0)
		return PINNED_OBJECT_TYPE;
	if(strcmp(name, "translatingobject") == 0)
		return TRANSLATING_OBJECT_TYPE;
	if(strcmp(name, "conditionaltranslatingobject") == 0)
		return CONDITIONAL_TRANSLATING_OBJECT_TYPE;
	if(strcmp(name, "ropedobject") == 0)
		return ROPED_OBJECT_TYPE;
	if(strcmp(name, "npcobject") == 0)
		return NPC_OBJECT_TYPE;
	if(strcmp(name, "toggleobject") == 0)
		return TOGGLE_OBJECT_TYPE;
	if(strcmp(name, "pressureobject") == 0)
		return PRESSURE_OBJECT_TYPE;
	return UNKNOWN_TYPE;	
}
void LevelReader::HandleObjectTag(TextureManager * textureManager, ObjectList ** objectList, MaterialList ** materialList, char * tag, int pos)
{
	char * element;
	
	element = GetNextElement(tag, &pos);
	
	if(element != NULL)
	{
		int type = GetObjectType(element);
		
		switch(type)
		{
			case BASE_OBJECT_TYPE:
				HandleBaseObjectTag(textureManager, objectList,materialList,tag, pos);
				break;
			case PINNED_OBJECT_TYPE:
				HandleRotatingObjectTag(textureManager, objectList, materialList, tag, pos);
				break;
			case TRANSLATING_OBJECT_TYPE:
				HandleTranslatingObjectTag(textureManager, objectList, materialList, tag, pos);
				break;
			case CONDITIONAL_TRANSLATING_OBJECT_TYPE:
				HandleConditionalTranslatingObjectTag(textureManager, objectList, materialList, tag, pos);
				break;
			case ROPED_OBJECT_TYPE:
				//HandleRopedObjectTag(objectList, stringList, tag, pos);
				break;
			case NPC_OBJECT_TYPE:
				//HandleNpcObjectTag(objectList, tag, pos);
				break;
			case TOGGLE_OBJECT_TYPE:
				//HandleToggleObjectTag(gameState, objectList, tag, pos);
				break; 
			case PRESSURE_OBJECT_TYPE:
				HandlePressureObjectTag(textureManager, objectList, materialList, tag, pos);
				break;
			case UNKNOWN_TYPE:
			default:
				printf("Unknown object type - %s\n", element);
				break;
		}
		free(element);
		element = NULL;
	}
}
void LevelReader::HandleMaterialTag(MaterialList ** materialList, char * tag, int pos)
{
	char * name = ReadNextString(tag, &pos);
	char * fileName = ReadNextString(tag, &pos);
	float bounciness = ReadNextFloat(tag, &pos);
	float friction = ReadNextFloat(tag, &pos);
	bool isGrabbable = ReadNextInt(tag, &pos) > 0;
	float size = ReadNextFloat(tag, &pos);
	
	Material * material = new Material(name, fileName, bounciness, friction, isGrabbable, size);
	
	if(!*materialList)
	{
		*materialList = new MaterialList(material);
	}
	else
	{
		(*materialList)->Add(material);
	}
}
void LevelReader::HandlePressureObjectTag(TextureManager * textureManager, ObjectList ** objectList, MaterialList ** materialList, char * tag, int pos)
{
	char * name = ReadNextString(tag, &pos);
	int numVertices = ReadNextInt(tag, &pos);
	Vector2D * vertices = ReadNextVectorArray(numVertices, tag, &pos);
	Vector2D position = ReadNextCoordinate(tag, &pos);
	float mass = ReadNextFloat(tag, &pos);
	bool isGrabbable = ReadNextInt(tag, &pos) > 0;	
	Vector2D movementAxis = ReadNextCoordinate(tag, &pos);
	Vector2D movementEndpoints = ReadNextCoordinate(tag, &pos);
	bool offRight = ReadNextInt(tag, &pos) > 0;	
	bool sticky = ReadNextInt(tag, &pos) > 0;	
	float springConstant = ReadNextFloat(tag, &pos);				
	char * textureName = ReadNextString(tag, &pos);
	Vector2D * textureVertices = ReadNextVectorArray(numVertices, tag, &pos);
	
	VertexList * vertexList = new VertexList();
	VertexList * pathList = new VertexList();
	if(vertices)
	{
		int i = 0;
		for(i=0; i < numVertices; i++)
		{
			vertexList->Add(new Vertex(vertices[i] + position));	
		}
		
		pathList->Add(new Vertex(position - movementAxis * movementEndpoints.y));
		pathList->Add(new Vertex(position + movementAxis * movementEndpoints.x));
		//PressureObject(TextureManager * textureManager, char * name, VertexList * vertices, char * textureName, Vector2D * textureVertices, Vector2D position, VertexList * path, float mass, float speed, bool offRight, bool sticky, bool isGrabbable);
		
		PressureObject * object = new PressureObject(textureManager,name,vertexList,textureName, textureVertices, 
													 position, pathList, mass, springConstant, offRight, sticky, 
													 isGrabbable);
	
		if(*objectList)
		{
			(*objectList)->Add(object->GetPath());
			(*objectList)->Add(object);
			
		}
		else
		{	
			*objectList = new ObjectList(object->GetPath());
			(*objectList)->Add(object);
		}
		delete vertices;
		vertices = NULL;
	}
}
void LevelReader::HandleTranslatingObjectTag(TextureManager * textureManager, ObjectList ** objectList, MaterialList ** materialList, char * tag, int pos)
{
	char * materialType = ReadNextString(tag, &pos);
	float textureRotation = ReadNextFloat(tag, &pos);
	int numPoints = ReadNextInt(tag, &pos);
	Vector2D * vertices = ReadNextVectorArray(numPoints, tag, &pos);
	float speed = ReadNextFloat(tag, &pos);
	float mass = ReadNextFloat(tag, &pos);
	int numPath = ReadNextInt(tag, &pos);
	Vector2D * path = ReadNextVectorArray(numPath, tag, &pos);
	
	VertexList * vertexList = new VertexList();
	VertexList * pathList = new VertexList();
	if(vertices && path)
	{
		int i = 0;
		for(i=0; i < numPoints; i++)
		{
			vertexList->Add(new Vertex(vertices[i] + path[0]));	
		}
		for(i=numPath-1; i > 0; i--)
		{
			pathList->Add(new Vertex(path[i]));
		}
		TranslatingObject * object = NULL;
		
		
		object = new TranslatingObject(textureManager, textureRotation, vertexList, (*materialList)->GetMaterial(materialType), path[0], pathList, mass, speed);
		
		if(*objectList)
		{
			(*objectList)->Add(object->GetPath());
			(*objectList)->Add(object);
			
		}
		else
		{	
			*objectList = new ObjectList(object->GetPath());
			(*objectList)->Add(object);
		}
		delete vertices;
		vertices = NULL;
	}
}
void LevelReader::HandleConditionalTranslatingObjectTag(TextureManager * textureManager, ObjectList ** objectList, MaterialList ** materialList, char * tag, int pos)
{
	char * condition = ReadNextString(tag, &pos);
	char * materialType = ReadNextString(tag, &pos);
	float textureRotation = ReadNextFloat(tag, &pos);
	int numPoints = ReadNextInt(tag, &pos);
	Vector2D * vertices = ReadNextVectorArray(numPoints, tag, &pos);
	float speed = ReadNextFloat(tag, &pos);
	float mass = ReadNextFloat(tag, &pos);
	int numPath = ReadNextInt(tag, &pos);
	Vector2D * path = ReadNextVectorArray(numPath, tag, &pos);
	
	VertexList * vertexList = new VertexList();
	VertexList * pathList = new VertexList();
	if(vertices && path)
	{
		int i = 0;
		for(i=0; i < numPoints; i++)
		{
			vertexList->Add(new Vertex(vertices[i] + path[0]));	
		}
		for(i=numPath-1; i > 0; i--)
		{
			pathList->Add(new Vertex(path[i]));
		}
		ConditionalTranslatingObject * object = NULL;
		
		
		object = new ConditionalTranslatingObject(textureManager, textureRotation, vertexList, (*materialList)->GetMaterial(materialType),
												  path[0], pathList, mass, speed, condition);
		
		if(*objectList)
		{
			(*objectList)->Add(object->GetPath());
			(*objectList)->Add(object);
			
		}
		else
		{	
			*objectList = new ObjectList(object->GetPath());
			(*objectList)->Add(object);
		}
		delete vertices;
		vertices = NULL;
	}
}
void LevelReader::HandleRotatingObjectTag(TextureManager * textureManager, ObjectList ** objectList, MaterialList ** materialList, char * tag, int pos)
{
	char * materialType = ReadNextString(tag, &pos);
	float textureRotation = ReadNextFloat(tag, &pos);
	int numPoints = ReadNextInt(tag, &pos);
	Vector2D * vertices = ReadNextVectorArray(numPoints, tag, &pos);
	Vector2D position = ReadNextCoordinate(tag, &pos);
	Vector2D rotationPoint = ReadNextCoordinate(tag, &pos);
	float mass = ReadNextFloat(tag, &pos);
	
	VertexList * vertexList = new VertexList();
	if(vertices)
	{
		for(int i=0; i < numPoints; i++)
		{
			vertexList->Add(new Vertex(vertices[i] + position));	
		}
		Object * object = NULL;
		
		
		object = new RotatingObject(textureManager, textureRotation, Vertex(position),Vertex(rotationPoint),vertexList, (*materialList)->GetMaterial(materialType), mass);
		
		if(*objectList)
		{
			(*objectList)->Add(object);
		}
		else
		{	
			*objectList = new ObjectList(object);
		}
		delete vertices;
		vertices = NULL;
	}
}
void LevelReader::HandleBaseObjectTag(TextureManager * textureManager, ObjectList ** objectList, MaterialList ** materialList, char * tag, int pos)
{
	printf("Handling base object\n");
	char * materialType = ReadNextString(tag, &pos);
	float textureRotation = ReadNextFloat(tag, &pos);
	int numPoints = ReadNextInt(tag, &pos);
	Vector2D * vertices = ReadNextVectorArray(numPoints, tag, &pos);
	Vector2D position = ReadNextCoordinate(tag, &pos);
	bool isPinned  = ReadNextInt(tag,&pos) > 0;
	float mass = ReadNextFloat(tag, &pos);
	
	VertexList * vertexList = new VertexList();
	if(vertices)
	{
		for(int i=0; i < numPoints; i++)
		{
			vertexList->Add(new Vertex(vertices[i] + position));	
		}
		Object * object = NULL;
		
		if(!isPinned)
			object = new ClosedObject(textureManager, textureRotation, Vertex(position),vertexList, (*materialList)->GetMaterial(materialType), mass);
		else
			object = new PinnedObject(textureManager, textureRotation, Vertex(position),vertexList, (*materialList)->GetMaterial(materialType), mass);
			
		if(*objectList)
		{
			(*objectList)->Add(object);
		}
		else
		{	
			*objectList = new ObjectList(object);
		}
		delete vertices;
		vertices = NULL;
	}
}
int LevelReader::GetCharacterType(char * element)
{
	if(strcmp(element, "base")==0)
		return BASIC_CHARACTER_TYPE;
	return UNKNOWN_TYPE;
}
void LevelReader::HandleCharacterObjectTag(TextureManager * textureManager, ObjectList ** objectList, char * tag, int pos)
{
	char * element;
	
	element = GetNextElement(tag, &pos);
	
	if(element != NULL)
	{
		int type = GetCharacterType(element);
		
		switch(type)
		{
			case BASIC_CHARACTER_TYPE:
				HandleBasicCharacterTag(textureManager, objectList,tag, pos);
				break;
			case UNKNOWN_TYPE:
			default:
				printf("Unknown object type - %s\n", element);
				break;
		}
		free(element);
		element = NULL;
	}
}
void LevelReader::HandleBasicCharacterTag(TextureManager * textureManager, ObjectList ** objectList, char * tag, int pos)
{
	printf("LevelManager::HandleBasicCharacterTag\n");
	Vector2D position = BasicFileReader::ReadNextCoordinate(tag, &pos);
	char * skeletonFile = BasicFileReader::ReadNextString(tag, &pos);
	bool isGrabbable = BasicFileReader::ReadNextInt(tag, &pos) > 0;
	int numEdges = BasicFileReader::ReadNextInt(tag, &pos);
	NavigationEdgeList * list = new NavigationEdgeList();
	for(int i = 0; i < numEdges; i++)
	{
		printf("loop %d\n", i);
		NavigationEdge * edge = ReadNextNavigationEdge(textureManager, objectList, tag, &pos);
		if(edge)
			list->ConnectEdge(edge);
	}
	printf("HERE\n");
	CharacterObject * object = new CharacterObject(*objectList, textureManager,Vertex(position),
			      									isGrabbable, skeletonFile,list);
	list->SetParent(object);
	if(*objectList)
		(*objectList)->Add(object);
	else
		(*objectList) = new ObjectList(object);
			      	
}
NavigationEdge * LevelReader::ReadNextNavigationEdge(TextureManager * textureManager, ObjectList ** objectList, char * tag, int * pos)
{
	Vector2D ep1 = ReadNextCoordinate(tag, pos);
	Vector2D ep2 = ReadNextCoordinate(tag, pos);
	char * r2lLocomotion = ReadNextString(tag,pos);
	char * l2rLocomotion = ReadNextString(tag,pos);
	int numTags = ReadNextInt(tag,pos);
	char ** behaviorTags = NULL;
	if(numTags > 0)
	{
		behaviorTags = (char**)malloc(sizeof(char*)*numTags);
		for(int i=0; i < numTags; i++)
		{
			char * bTag = ReadNextString(tag,pos);
			printf("tag %d = %s\n", i, bTag);
			behaviorTags[i] = bTag;
		}
	}
	NavigationEdge * edge = new NavigationEdge(textureManager,new Vertex(ep1), new Vertex(ep2), r2lLocomotion,
						l2rLocomotion, behaviorTags, numTags);
				
	if(*objectList)
		(*objectList)->Add(edge);
	else
		(*objectList) = new ObjectList(edge);
			      	
	return edge;
}
